﻿unit SK0001Comm;

interface

uses
  FastMM4,
  FastMove,
  System.SysUtils,
  System.DateUtils,
  System.Classes,
  System.Types,
  Winapi.Windows,
  Vcl.Forms,
  Vcl.Controls,
  Vcl.Dialogs,
  Data.DB,
  Data.Win.ADODB,
  Datasnap.DBClient,
  Datasnap.Provider,
  TypInfo,
  SysConst,
  {$IFDEF UNICODE}
  AnsiStrings,
  {$ENDIF }
  //Base64编码
  Soap.EncdDecdSelf,
  UserConnection,
  DBAccessor,
  DllPlugin,
  QBParcel;
  
 type
  ePretreatmentMode =(pmNone,pmAdd,pmEdit,pmDelete,pmSendApprove,pmApprove,pmUnApprove,
                      pmCancel,pmUnCancel,pmPrint,pmUnPrint,pmAccount,pmUnAccount,
                      pmIgnore,pmUnIgnore,pmUse,pmUnUse);
  
  eDBLoadMode=(dblmNone,dblmTable,dblmView);

  TSK0001Comm=class(TObject)
  private
  protected
  public
    class function GetTimeStampMs:Int64;
    class function GetGUID:string;
    class function SetObjGUID(const iID:string):string;
    class function GenerateId(const iDBConn:TUserConnection; const iDataBaseID,iTableName,iKeyFieldName,iFilterCondition,iFieldMask,iConstList:string; out oNewId,oError:string):Boolean;
    class function FreeId(const iDBConn:TUserConnection; const iDataBaseID,iTableName,iKeyFieldName,iFilterCondition,iFreeId:string; out oError:string):Boolean;
    class function ExecuteSQLs(const iDBConn:TUserConnection; const iDataBaseID:string; const iSQLs:TStrings; out oError:string):Boolean;overload;
    class function ExecuteSQLs(const iDBConn:TADOConnection; const iDataBaseID:string; const iSQLs:TStrings; out oError:string):Boolean;overload;
    class function GetCDS(const iDBConn:TUserConnection; const iDataBaseID,iSQL:string; var iCds:TClientDataSet; out oError:string):Boolean;overload;
    class function GetCDS(const iDBConn:TADOConnection; const iDataBaseID,iSQL:string; var iCds:TClientDataSet; out oError:string):Boolean;overload;
    class function ReadSimpleData(const iDBConn:TUserConnection; const iDataBaseID:string; const iSQL:string; out oResultStr:string; out oError:string):Boolean;overload;
    class function ReadSimpleData(const iDBConn:TADOConnection; const iDataBaseID:string; const iSQL:string; out oResultStr:string; out oError:string):Boolean;overload;
    class function AnsiStrToMemStream(const Str:AnsiString; var Stream:TMemoryStream):Boolean;
    class function MemStreamToAnsiStr(const Stream:TMemoryStream; const Offset:Int64=0; const Count:Int64=0):AnsiString;
    class function MemStreamToBase64Str(const InStream:TMemoryStream):AnsiString;
    class function Base64StrToMemStream(const Base64Str:AnsiString; var OutStream:TMemoryStream):Boolean;
    class function BeforeStr(Str:AnsiString; ReferStr:AnsiString):AnsiString;
    class function AfterStr(Str:AnsiString; ReferStr:AnsiString):AnsiString;
    class function HexToStr(S: String): String;
    class function StrToHex(S: String): String;
  end;
  
implementation

class function TSK0001Comm.GetGUID:string;
var
  guid:TGUID;
begin
  CreateGUID(guid);
  Result:=GUIDToString(guid);
  Result := StringReplace(Result,'-','',[rfReplaceAll]);
  Result := StringReplace(Result,'{','',[rfReplaceAll]);
  Result := StringReplace(Result,'}','',[rfReplaceAll]);
end;

class function TSK0001Comm.SetObjGUID(const iID:string):string;
begin
  if iID=EmptyStr then
    Result:=GetGUID
  else
    Result:=iID;
end;

class function TSK0001Comm.GetTimeStampMs:Int64;
var
  StartTime:TDateTime;
  EndTime:TDateTime;
begin
  StartTime := EncodeDateTime( 1970, 1, 1, 0, 0, 0, 0 );
  EndTime:=IncHour(Now,-8);
  Result := MilliSecondsBetween(StartTime,EndTime);
end;

class function TSK0001Comm.GenerateId(const iDBConn:TUserConnection; const iDataBaseID,iTableName,iKeyFieldName,iFilterCondition,iFieldMask,iConstList:string; out oNewId,oError:string):Boolean;
var
  DllPlugin:TDllPlugin;
  InParcel,OutParcel:TQBParcel;
  ErrorCode,ErrorText:string;
begin
  if (iDBConn<>nil) and not SameStr(Trim(iDataBaseID),EmptyStr) and
    not SameStr(Trim(iTableName),EmptyStr) and not SameStr(Trim(iKeyFieldName),EmptyStr) and
    not SameStr(Trim(iFieldMask),EmptyStr) and not SameStr(Trim(iConstList),EmptyStr) then
  begin
    DllPlugin:=TDllPlugin.Create(nil);
    InParcel:=TQBParcel.Create;
    OutParcel:=TQBParcel.Create;
    try
      DllPlugin.UserConnection:=iDBConn;
      DllPlugin.TargetNodeId:=iDBConn.UserNodeId;
      DllPlugin.TaskTimeout:=60;
      InParcel.PutAnsiStringGoods('Fn','GenerateIdByDBA');
      InParcel.PutAnsiStringGoods('ServerName',EmptyAnsiStr);
      InParcel.PutAnsiStringGoods('DBID',AnsiString(iDataBaseID));
      InParcel.PutAnsiStringGoods('TableName',AnsiString(iTableName));
      InParcel.PutAnsiStringGoods('KeyFieldName',AnsiString(iKeyFieldName));
      InParcel.PutAnsiStringGoods('FilterCondition',AnsiString(iFilterCondition));
      InParcel.PutAnsiStringGoods('FieldMask',AnsiString(iFieldMask));
      InParcel.PutAnsiStringGoods('ConstList',AnsiString(iConstList));
      if DllPlugin.BinaryRPC('DBServiceManager','',InParcel,OutParcel) then
      begin
        if OutParcel.GoodsExists('error') then
        begin
          oError:=Format(string('调用DBServiceManager.GenerateIdByDBA函数失败,错误内容:%s'),[string(OutParcel.GetAnsiStringGoods('error'))]);
          Result:=False;
          Exit;
        end else begin
          oNewId:=string(OutParcel.GetAnsiStringGoods('ResultValue'));
        end;
      end else begin
        DllPlugin.GetLastError(ErrorCode,ErrorText);
        oError:=Format(string('调用DBServiceManager.GenerateIdByDBA函数失败 错误代码:%s 错误内容:%s'),[ErrorCode,ErrorText]);
        Result:=False;
        Exit;
      end;
    finally
      DllPlugin.Free;
      InParcel.Free;
      OutParcel.Free;
    end;
  end else begin
    oError:=Format(string('调用GenerateId失败,传入的参数或对象为空'),[]);
    Result:=False;
    Exit;
  end;
  Result:=True;
end;

class function TSK0001Comm.FreeId(const iDBConn:TUserConnection; const iDataBaseID,iTableName,iKeyFieldName,iFilterCondition,iFreeId:string; out oError:string):Boolean;
var
  DllPlugin:TDllPlugin;
  InParcel,OutParcel:TQBParcel;
  ErrorCode,ErrorText:string;
begin
  if (iDBConn<>nil) and not SameStr(Trim(iDataBaseID),EmptyStr) and
    not SameStr(Trim(iTableName),EmptyStr) and not SameStr(Trim(iKeyFieldName),EmptyStr) and
    not SameStr(Trim(iFreeId),EmptyStr) then
  begin
    DllPlugin:=TDllPlugin.Create(nil);
    InParcel:=TQBParcel.Create;
    OutParcel:=TQBParcel.Create;
    try
      DllPlugin.UserConnection:=iDBConn;
      DllPlugin.TargetNodeId:=iDBConn.UserNodeId;
      DllPlugin.TaskTimeout:=60;
      InParcel.PutAnsiStringGoods('Fn','FreeUnusedIdByDBA');
      InParcel.PutAnsiStringGoods('ServerName',EmptyAnsiStr);
      InParcel.PutAnsiStringGoods('DBID',AnsiString(iDataBaseID));
      InParcel.PutAnsiStringGoods('TableName',AnsiString(iTableName));
      InParcel.PutAnsiStringGoods('KeyFieldName',AnsiString(iKeyFieldName));
      InParcel.PutAnsiStringGoods('FilterCondition',AnsiString(iFilterCondition));
      InParcel.PutAnsiStringGoods('IdToFree',AnsiString(iFreeId));
      if DllPlugin.BinaryRPC('DBServiceManager','',InParcel,OutParcel) then
      begin
        if OutParcel.GoodsExists('error') then
        begin
          oError:=Format(string('调用DBServiceManager.FreeUnusedIdByDBA函数失败,错误内容:%s'),[string(OutParcel.GetAnsiStringGoods('error'))]);
          Result:=False;
          Exit;
        end else begin

        end;
      end else begin
        DllPlugin.GetLastError(ErrorCode,ErrorText);
        oError:=Format(string('调用DBServiceManager.FreeUnusedIdByDBA函数失败 错误代码:%s 错误内容:%s'),[ErrorCode,ErrorText]);
        Result:=False;
        Exit;
      end;
    finally
      DllPlugin.Free;
      InParcel.Free;
      OutParcel.Free;
    end;
  end else begin
    oError:=Format(string('调用FreeId失败,传入的参数或对象为空'),[]);
    Result:=False;
    Exit;
  end;
  Result:=True;
end;

class function TSK0001Comm.ExecuteSQLs(const iDBConn:TUserConnection; const iDataBaseID:string; const iSQLs:TStrings; out oError:string):Boolean;
var
  Sql:string;
  DBA:TDBAccessor;
  ErrorCode,ErrorText:string;
  RecordsAffected:Integer;

  DllPlugin:TDllPlugin;
  InParcel,OutParcel:TQBParcel;
  MemStream:TMemoryStream;
begin
  Result:=False;
  if (iSQLs<>nil) and (iSQLs.Count>0) and iSQLs.InheritsFrom(TStringList) then
  begin
    if iSQLs.Count<=100 then
    begin
      DBA := TDBAccessor.Create(nil);
      DBA.UserConnection := iDBConn;
      DBA.TargetNodeId := iDBConn.UserNodeId;
      DBA.TargetDatabaseId :=AnsiString(iDataBaseID);
      DBA.BeginMergeTasks;
      try
        for Sql in iSQLs do
        begin
          if not DBA.ExecuteCommand(Sql,RecordsAffected) then
          begin
            DBA.AbortMergeTasks;
            DBA.GetLastError(ErrorCode,ErrorText);
            oError:=Format(string('执行批量语句错误! 错误代码:%s 错误内容:%s'),[ErrorCode,ErrorText]);
            DBA.Free;
            Exit;
          end;
        end;
        DBA.CommitMergeTasks;
        Result:=True;
      except
        DBA.AbortMergeTasks;
        DBA.GetLastError(ErrorCode,ErrorText);
        oError:=Format(string('执行批量语句错误! 错误代码:%s 错误内容:%s'),[ErrorCode,ErrorText]);
        DBA.Free;
        raise;
      end;
      DBA.Free;
    end else begin
      DllPlugin:=TDllPlugin.Create(nil);
      InParcel:=TQBParcel.Create;
      OutParcel:=TQBParcel.Create;
      MemStream:=TMemoryStream.Create;
      try
        DllPlugin.UserConnection:=iDBConn;
        DllPlugin.TargetNodeId:=iDBConn.UserNodeId;
        DllPlugin.TaskTimeout:=60;
        InParcel.PutAnsiStringGoods(AnsiString('Fn'),AnsiString('ExecuteBatchSqlByDB'));
        InParcel.PutAnsiStringGoods(AnsiString('DBID'),AnsiString(iDataBaseID));
        iSQLs.SaveToStream(MemStream);
        InParcel.PutStreamGoods(AnsiString('SQLs'),MemStream);

        if DllPlugin.BinaryRPC(AnsiString('DBServiceManager'),EmptyAnsiStr,InParcel,OutParcel) then
        begin
          if OutParcel.GoodsExists('error') then
          begin
            oError:=Format(string('执行批量语句错误! 错误内容:%s'),[string(OutParcel.GetAnsiStringGoods('error'))]);
            Result:=False;
            Exit;
          end else begin
            Result:=True;
          end;
        end else begin
          DllPlugin.GetLastError(ErrorCode,ErrorText);
          oError:=Format(string('执行批量语句调用%s插件%s错误! 错误代码:%s 错误内容:%s:!'),['DBServiceManager','ExecuteBatchSqlByADO',ErrorCode,ErrorText]);
          Result:=False;
          Exit;
        end;
      finally
        DllPlugin.Free;
        InParcel.Free;
        OutParcel.Free;
        MemStream.Free;
      end;
    end;
  end else begin
    oError:=Format(string('执行SQL语句时,传入SQLs列表对象为NULL或者记录数为0'),[])
  end;
end;

class function TSK0001Comm.ExecuteSQLs(const iDBConn:TADOConnection; const iDataBaseID:string; const iSQLs:TStrings; out oError:string):Boolean;
var
  SQL:string;
begin
  if (iDBConn<>nil) and
    (iSQLs<>nil) then
  begin
    iDBConn.BeginTrans;
    try
      for SQL in iSQLs do
      begin
        iDBConn.Execute(SQL);
      end;
      iDBConn.CommitTrans;
    except
      iDBConn.RollbackTrans;
      oError:=Format(string('执行批量语句错误失败!'),[]);
      raise;
    end;
  end else begin
    oError:=Format(string('执行批量语句错误! 错误内容:%s'),['调用参数或对象为空!']);
    Result:=False;
    Exit;
  end;
  Result:=True;
end;

class function TSK0001Comm.GetCDS(const iDBConn:TUserConnection; const iDataBaseID,iSQL:string; var iCds:TClientDataSet; out oError:string):Boolean;
var
  DBA:TDBAccessor;
  ErrorCode,ErrorText:string;
begin
  if iCds<>nil then
  begin
    DBA := TDBAccessor.Create(nil);
    DBA.UserConnection := iDBConn;
    DBA.TargetNodeId := iDBConn.UserNodeId;
    DBA.TargetDatabaseId :=AnsiString(iDataBaseID);
    try
      if not DBA.ReadDataset(iSQL,iCDS) then
      begin
        DBA.GetLastError(ErrorCode,ErrorText);
        oError:=Format(string('获取数据集失败!错误代码:%s 错误内容:%s'),[ErrorCode,ErrorText]);
        Result:=False;
        Exit;
      end;
    except
      DBA.GetLastError(ErrorCode,ErrorText);
      oError:=Format(string('获取数据集失败!错误代码:%s 错误内容:%s'),[ErrorCode,ErrorText]);
      DBA.Free;
      raise;
    end;
    DBA.Free;
  end;
  Result:=True;
end;

class function TSK0001Comm.GetCDS(const iDBConn:TADOConnection; const iDataBaseID,iSQL:string; var iCds:TClientDataSet; out oError:string):Boolean;
//var
//  AdoQuery: TADOQuery;
//  DataSetProvider:TDataSetProvider;
//begin
//  if (iDBConn<>nil) and
//    (iCds<>nil) and
//    (not SameStr(Trim(iSQL),EmptyStr)) then
//  begin
//    AdoQuery:=TADOQuery.Create(nil);
//    DataSetProvider:=TDataSetProvider.Create(nil);
//    try
//      iCds.ProviderName:=DataSetProvider.Name;
//      AdoQuery.Connection:=iDBConn;
//      DataSetProvider.DataSet:=AdoQuery;
//      AdoQuery.SQL.Clear;
//      AdoQuery.SQL.Add(iSQL);
//      AdoQuery.Open;
//      iCds.Close;
//      iCds.Fields.Clear;
//      iCds.Data := DataSetProvider.Data;
//    except
//      AdoQuery.Free;
//      DataSetProvider.Free;
//      oError:=Format(string('获取数据集失败!错误SQL语句:%s'),[iSQL]);
//      raise;
//    end;
//    AdoQuery.Free;
//    DataSetProvider.Free;
//  end else begin
//    oError:=Format(string('获取数据集失败!错误内容:%s'),['调用参数或对象为空!']);
//    Result:=False;
//    Exit;
//  end;
//  Result:=True;
//end;
var
  AdoQuery: TADOQuery;
  iField:Integer;
begin
  if (iDBConn<>nil) and
    (iCds<>nil) and
    (not SameStr(Trim(iSQL),EmptyStr)) then
  begin
    AdoQuery:=TADOQuery.Create(nil);
    try
      AdoQuery.Connection:=iDBConn;
      AdoQuery.SQL.Clear;
      AdoQuery.SQL.Add(iSQL);
      AdoQuery.Open;

      if AdoQuery.Fields.Count>0 then
      begin
        iCds.Close;
        iCds.Fields.Clear;
        iCds.FieldDefs.Clear;
        for iField:=0 to AdoQuery.Fields.Count-1 do
        begin
//          iCds.FieldDefs.Add(AdoQuery.Fields[iField].DisplayName,
//                            AdoQuery.Fields[iField].DataType,
//                            AdoQuery.Fields[iField].Size);
          with iCds.FieldDefs.AddFieldDef do
          begin
            Name:=AdoQuery.Fields[iField].DisplayName;
            DataType:=AdoQuery.Fields[iField].DataType;
            Size:=AdoQuery.Fields[iField].Size;
          end;
        end;
         //建立结构。
        iCds.CreateDataSet;

        if not AdoQuery.IsEmpty then
        begin
          //循环加入列表中
          AdoQuery.First;
          while not AdoQuery.Eof do
          begin
            iCds.Append;
            iCds.CopyFields(AdoQuery);
//            for iField:=0 to AdoQuery.Fields.Count-1 do
//            begin
//              iCds.FieldByName(AdoQuery.Fields[iField].DisplayName).Value:=AdoQuery.FieldByName(AdoQuery.Fields[iField].DisplayName).Value;
//            end;
            iCds.Post;
            AdoQuery.Next;
          end;
        end;
      end;
      Result:=True;
    except
      AdoQuery.Free;
      oError:=Format(string('获取数据集失败!错误SQL语句:%s'),[iSQL]);
      raise;
    end;
    AdoQuery.Free;
  end else begin
    oError:=Format(string('获取数据集失败!错误内容:%s'),['调用参数或对象为空!']);
    Result:=False;
    Exit;
  end;
  Result:=True;
end;

class function TSK0001Comm.ReadSimpleData(const iDBConn:TUserConnection; const iDataBaseID:string; const iSQL:string; out oResultStr:string; out oError:string):Boolean;
var
  DBA:TDBAccessor;
  ErrorCode,ErrorText:string;
begin
  if iDBConn<>nil then
  begin
    DBA := TDBAccessor.Create(nil);
    DBA.UserConnection := iDBConn;
    DBA.TargetNodeId := iDBConn.UserNodeId;
    DBA.TargetDatabaseId :=AnsiString(iDataBaseID);
    try
      if not DBA.ReadSimpleResult(iSQL,oResultStr) then
      begin
        DBA.GetLastError(ErrorCode,ErrorText);
        oError:=Format(string('获取简单数据失败!错误代码:%s 错误内容:%s'),[ErrorCode,ErrorText]);
        Result:=False;
        Exit;
      end;
    except
      DBA.GetLastError(ErrorCode,ErrorText);
      oError:=Format(string('获取简单数据失败!错误代码:%s 错误内容:%s'),[ErrorCode,ErrorText]);
      DBA.Free;
      raise;
    end;
    DBA.Free;
  end;
  Result:=True;
end;

class function TSK0001Comm.ReadSimpleData(const iDBConn:TADOConnection; const iDataBaseID:string; const iSQL:string; out oResultStr:string; out oError:string):Boolean;
var
  AdoQuery: TADOQuery;
begin
  oResultStr:=EmptyStr;
  if (iDBConn<>nil) and
    (not SameStr(Trim(iSQL),EmptyStr)) then
  begin
    AdoQuery:=TADOQuery.Create(nil);
    try
      AdoQuery.Connection:=iDBConn;
      AdoQuery.SQL.Add(iSQL);
      AdoQuery.Open;
      if AdoQuery.Fields.Count=1 then
      begin
        oResultStr:=AdoQuery.Fields[0].AsString;
      end;
    except
      AdoQuery.Free;
      oError:=Format(string('执行SQL语句错误失败!'),[]);
      raise;
    end;
    AdoQuery.Free;
  end else begin
    oError:=Format(string('读取简单数据错误! 错误内容:%s'),['调用参数或对象为空!']);
    Result:=False;
    Exit;
  end;
  Result:=True;
end;

class function TSK0001Comm.AnsiStrToMemStream(const Str:AnsiString; var Stream:TMemoryStream):Boolean;
begin
  Result:=False;
  try
    if Stream<>nil then
    begin
      Stream.Position:=0;
      Stream.WriteBuffer(Str[1],length(Str));
      Stream.Position:=0;
      Result:=True;
    end;
  except
    raise;
  end;
end;

class function TSK0001Comm.MemStreamToAnsiStr(const Stream:TMemoryStream; const Offset:Int64; const Count:Int64):AnsiString;
var
  TempStr:AnsiString;
begin
  Result:=EmptyAnsiStr;
  try
    if (Stream<>nil) and
      (Offset>=0) and
      (Offset<=Stream.Size-1) and
      (Count>=0) and
      ((Offset+Count)<=Stream.Size) then
    begin
      if Count>0 then
      begin
        SetLength(TempStr,Count);
      end else begin
        SetLength(TempStr,Stream.Size);
      end;
      Stream.Position:=Offset;
      Stream.ReadBuffer(TempStr[1],Length(TempStr));
      Stream.Position:=0;
      Result:=TempStr;
    end;
  finally
  end;
end;

class function TSK0001Comm.MemStreamToBase64Str(const InStream:TMemoryStream):AnsiString;
var
  Base64Stream:TMemoryStream;
begin
  Result := EmptyAnsiStr;
  Base64Stream:=TMemoryStream.Create;
  try
    if InStream<>nil then
    begin
      InStream.Position:=0;
      Base64Stream.Position:=0;
      EncodeStream(InStream,Base64Stream);
      Base64Stream.Position:=0;
      Result:=MemStreamToAnsiStr(Base64Stream);
      InStream.Position:=0;
      Base64Stream.Position:=0;
    end;
  finally
    Base64Stream.Free;
  end;
end;

class function TSK0001Comm.Base64StrToMemStream(const Base64Str:AnsiString; var OutStream:TMemoryStream):Boolean;
var
  Base64Stream:TMemoryStream;
begin
  Result:=False;
  Base64Stream:=TMemoryStream.Create;
  try
    if (Base64Str<>EmptyAnsiStr) and (OutStream<>nil) then
    begin
      AnsiStrToMemStream(Base64Str,Base64Stream);
      Base64Stream.Position:=0;
      OutStream.Position:=0;
      DecodeStream(Base64Stream,OutStream);
      OutStream.Position:=0;
      Result:=True;
    end;
  finally
    Base64Stream.Free;
  end;
end;

class function TSK0001Comm.BeforeStr(Str:AnsiString; ReferStr:AnsiString):AnsiString;
var
  Ops:Integer;
begin
  Ops:=AnsiPos(ReferStr,Str);
  if Ops>0 then
  begin
    Result:=LeftStr(Str,Ops-1);
  end else begin
    Result:=EmptyAnsiStr;
  end;
end;

class function TSK0001Comm.AfterStr(Str:AnsiString; ReferStr:AnsiString):AnsiString;
var
  Ops:Integer;
begin
  Ops:=AnsiPos(ReferStr,Str);
  if Ops>0 then
  begin
    Result:=RightStr(Str,Length(Str)-(Ops+Length(ReferStr)-1));
  end else begin
    Result:=EmptyAnsiStr;
  end;
end;

{$REGION '16进制字符串转换成字符串（支持汉字）'}
class function TSK0001Comm.HexToStr(S: String): String;
Var
  Stream: TMemoryStream;
  Value: TStringStream;
  Pos: Integer;
Begin
  Result := '';
  If Length(S) > 0 Then
  Begin
    Stream := TMemoryStream.Create;
    Value := TStringStream.Create('');
    Try
      Pos := Stream.Position;
      Stream.SetSize(Stream.Size + Length(S) Div 2);
      HexToBin(PChar(S), PChar(Integer(Stream.Memory) + Stream.Position), Length(S) Div 2);
      Stream.Position := Pos;
      Value.CopyFrom(Stream, Length(S) Div 2);
      Result := Value.DataString;
    Finally
      Stream.Free;
      Value.Free;
    End;
  End;
End;
{$ENDREGION}

{$REGION '字符串转换成16进制字符串（支持汉字）'}
class function TSK0001Comm.StrToHex(S: String): String;
Var
  Stream: TMemoryStream;
  Value: TStringStream;
Begin
  If Length(S) > 0 Then
  Begin
    Value := TStringStream.Create(S);
    Try
      SetLength(Result, (Value.Size - Value.Position) * 2);
      If Length(Result) > 0 Then
      Begin
        Stream := TMemoryStream.Create;
        Try
          Stream.CopyFrom(Value, Value.Size - Value.Position);
          Stream.Position := 0;
          BinToHex(PChar(Integer(Stream.Memory) + Stream.Position), PChar(Result), Stream.Size - Stream.Position);
        Finally
          Stream.Free;
        End;
      End;
    Finally
      Value.Free;
    End;
  End;
End;
{$ENDREGION}

end.